import logging
import os
import zipfile
import contextlib

from pluginlib import PluginError
from pluginlib.plugin import Plugin
from pluginlib.context import Context


logger = logging.getLogger('pluginglib.manager')

class PluginNotFoundError(PluginError):
    pass

class DisabledPluginError(PluginError):
    pass

class PluginManager(object):
    '''Load/manage plugins.'''

    def __init__(self, path):
        self.plugins = {}
        self.context = Context()
        self.plugin_finder = PluginFinder(path)
        logger.debug("Created PluginManager with path %s" % path)

        self.plugin_finder.find_plugins(self._found_plugin)

    def _found_plugin(self, path, plugin_type):
        if path not in self.plugins:
            self.plugins[path] = Plugin.create(self, plugin_type, path)
        else:
            self.plugins[path].check_reload()

    def get_interface(self, iface_name):
        return self.context.get_interface(iface_name)

    def get_implementations(self, iface_name):
        return self.context.get_implementations(iface_name)

    def get_implementation(self, iface_name, impl_name):
        return self.context.get_implementation(iface_name, impl_name)

class PluginFinder(object):
    def __init__(self, path):
        self.path = path

    def find_plugins(self, on_plugin_found):
        '''List all available plugins without loading anything.'''
        for plugins_dir in self.path:
            self._reload_plugins_from_dir(plugins_dir, on_plugin_found)

    def _reload_plugins_from_dir(self, path_dir_name, on_plugin_found):
        for dir_name, sub_dirs_names, file_names in os.walk(path_dir_name):
            if 'plugin.conf' in file_names:

                # Stop recursing
                while sub_dirs_names:
                    sub_dirs_names.remove(0)

                on_plugin_found(dir_name, 'dir')
                continue

            # look for zipped plugins
            for file_name in file_names:
                file_path = os.path.join(dir_name, file_name)
                if self.__is_zip_plugin(file_path):
                    on_plugin_found(file_path, 'zip')

    def __is_zip_plugin(self, file_path):
        if zipfile.is_zipfile(file_path):
            with contextlib.closing(zipfile.ZipFile(file_path)) as zip_file:
                if 'plugin.conf' in zip_file.namelist():
                    return True
        return False

